#include "StdAfx.h"
#include "MsgStore.h"

#include "MAPIFolder.h"

#define INITGUID
// #define USES_IID_IMAPISup
// #define USES_IID_IMSProvider
#define USES_IID_IMsgStore
// #define USES_IID_IMAPIFolder
// #define USES_IID_IMessage
#define USES_IID_IMAPIProp
// #define USES_IID_IMAPITable
#include <initguid.h>
#include <mapiguid.h>


#define Log


CMsgStore::CMsgStore (LPMDB pObj) : m_cRef(1), m_pObj(pObj)
{
	m_pObj->AddRef();
	Log(true,"In Constructor of CMsgStore\n");
}


CMsgStore::~CMsgStore()
{
	m_pObj->Release();
	Log(true,"In Destructor of CMsgStore\n");
}


STDMETHODIMP CMsgStore::QueryInterface (REFIID riid, LPVOID * ppvObj)
{
	*ppvObj = NULL;

	// If the interface requested is supported by this object, return a pointer
	// to the provider, with the reference count incremented by one.
	if (riid == IID_IMsgStore || riid == IID_IMAPIProp || riid == IID_IUnknown)	{
		*ppvObj = (LPVOID)this;
		AddRef();
		return S_OK;
	}
	
	return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CMsgStore::AddRef()
{
	if (!m_pObj) return NULL;
	m_cRef++;
	return m_cRef;
}

STDMETHODIMP_(ULONG) CMsgStore::Release()
{
	Log(true,"CMsgStore::Release() called\n");

	if (!m_pObj) return NULL;

	m_cRef--;
	if (m_cRef == 0) {
		m_pObj->Release();
		delete this;
		return 0;
	}

	return m_cRef;
}

///////////////////////////////////////////////////////////////////////////////
// IMAPIProp virtual member functions implementation
//

STDMETHODIMP CMsgStore::GetLastError(HRESULT hError,
									 ULONG ulFlags,
									 LPMAPIERROR * ppMAPIError)
{
	Log(true,"CMsgStore::GetLastError\n");
	return m_pObj->GetLastError(
		hError,
		ulFlags,
		ppMAPIError);
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::SaveChanges()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Stub method. The store object itself is not transacted. Changes
//      occur immediately without the need for committing changes.
//
//    Return Value
//      S_OK always
//
STDMETHODIMP CMsgStore::SaveChanges(ULONG ulFlags)
{
	HRESULT hRes = S_OK;

	hRes = m_pObj->SaveChanges(ulFlags);

	return hRes;
}


///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::GetProps()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Return the value of the properties the client especified in the
//      property tag array. If the tag array is NULL, the user wants all
//      the properties.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::GetProps (LPSPropTagArray pPropTagArray,
								  ULONG ulFlags,
								  ULONG * pcValues,
								  LPSPropValue * ppPropArray)
{
	Log(true,"CMsgStore::GetProps\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->GetProps(
		pPropTagArray,
		ulFlags,
		pcValues,
		ppPropArray);

	Log(true,"CMsgStore::GetProps returned 0x%08X\n", hRes);
	return hRes;
}


///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::GetPropList()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Duplicate the tag array with ALL the available properties
//      on this object. Properties added with SetProps() will not be returned.
//      The must be explicitly requested in a GetProps() call.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::GetPropList (ULONG ulFlags,
									 LPSPropTagArray * ppAllTags)
{
	Log(true,"CMsgStore::GetPropList\n");

	HRESULT hRes = S_OK;

	hRes = m_pObj->GetPropList(
		ulFlags,
		ppAllTags);

	Log(true,"CMsgStore::GetPropList returned 0x%08X\n", hRes);
	return hRes;
}


///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::OpenProperty()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Stub method. Opening properties directly on the
//      store object is not supported.
//
//    Return Value
//      MAPI_E_NO_SUPPORT always.
//
STDMETHODIMP CMsgStore::OpenProperty (ULONG 	  ulPropTag,
									  LPCIID	  piid,
									  ULONG 	  ulInterfaceOptions,
									  ULONG 	  ulFlags,
									  LPUNKNOWN * ppUnk)
{
	Log(true, "CMsgStore::OpenProperty method called");
	HRESULT hRes = S_OK;

	hRes = m_pObj->OpenProperty(
		ulPropTag,
		piid,
		ulInterfaceOptions,
		ulFlags,
		ppUnk);

	Log(true,"CMsgStore::OpenProperty returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::SetProps()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Modify the value of the properties in the object. On IMsgStore
//      objects, changes to the properties are committed immediately.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::SetProps (ULONG 				cValues,
								  LPSPropValue			pPropArray,
								  LPSPropProblemArray * ppProblems)
{
	Log(true,"CMsgStore::SetProps\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->SetProps(
		cValues,
		pPropArray,
		ppProblems);

	Log(true,"CMsgStore::SetProps returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::DeleteProps()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Stub method. Clients are not allowed to delete properties in the
//      IMsgStore object.
//
//    Return Value
//      MAPI_E_NO_SUPPORT always
//
STDMETHODIMP CMsgStore::DeleteProps (LPSPropTagArray	   pPropTagArray,
									 LPSPropProblemArray * ppProblems)
{
	Log(true,"CMsgStore::DeleteProps\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->DeleteProps(
		pPropTagArray,
		ppProblems);

	Log(true,"CMsgStore::DeleteProps returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::CopyTo()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      MAPI 1.0 does not require IMsgStore to support copying itself to
//      other store, so always return MAPI_E_NO_SUPPORT.
//
//    Return Value
//      MAPI_E_NO_SUPPORT always.
//
STDMETHODIMP CMsgStore::CopyTo (ULONG				  ciidExclude,
								LPCIID				  rgiidExclude,
								LPSPropTagArray 	  pExcludeProps,
								ULONG				  ulUIParam,
								LPMAPIPROGRESS		  pProgress,
								LPCIID				  pInterface,
								LPVOID				  pDestObj,
								ULONG				  ulFlags,
								LPSPropProblemArray * ppProblems)
{
	Log(true,"CMsgStore::CopyTo\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->CopyTo(
		ciidExclude,
		rgiidExclude,
		pExcludeProps,
		ulUIParam,
		pProgress,
		pInterface,
		pDestObj,
		ulFlags,
		ppProblems);

	Log(true,"CMsgStore::CopyTo returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::CopyProps()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Stub method. Copying the provider's properties onto
//      another object is not supported.
//
//    Return Value
//      MAPI_E_NO_SUPPORT always
//
STDMETHODIMP CMsgStore::CopyProps (LPSPropTagArray		 pIncludeProps,
								   ULONG				 ulUIParam,
								   LPMAPIPROGRESS		 pProgress,
								   LPCIID				 pInterface,
								   LPVOID				 pDestObj,
								   ULONG				 ulFlags,
								   LPSPropProblemArray * ppProblems)
{
	Log(true,"CMsgStore::CopyProps\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->CopyProps(
		pIncludeProps,
		ulUIParam,
		pProgress,
		pInterface,
		pDestObj,
		ulFlags,
		ppProblems);

	Log(true,"CMsgStore::CopyProps returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::GetNamesFromIDs()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Return the list of names for the identifier list supplied by the
//      caller. This implementation defers to the common IMAPIProp
//      implementation to do the actual work.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::GetNamesFromIDs (LPSPropTagArray * ppPropTags,
										 LPGUID 		   pPropSetGuid,
										 ULONG			   ulFlags,
										 ULONG *		   pcPropNames,
										 LPMAPINAMEID **   pppPropNames)
{
	Log(true,"CMsgStore::GetNamesFromIDs\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->GetNamesFromIDs(
		ppPropTags,
		pPropSetGuid,
		ulFlags,
		pcPropNames,
		pppPropNames);

	Log(true,"CMsgStore::GetNamesFromIDs returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::GetIDsFromNames()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Return a property identifier for each named listed by the client. This
//      IMsgStore implementation defers to the common IMAPIProp
//      implementation to do the actual work.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::GetIDsFromNames (ULONG			   cPropNames,
										 LPMAPINAMEID *    ppPropNames,
										 ULONG			   ulFlags,
										 LPSPropTagArray * ppPropTags)
{
	Log(true,"CMsgStore::GetIDsFromNames\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->GetIDsFromNames(
		cPropNames,
		ppPropNames,
		ulFlags,
		ppPropTags);

	Log(true,"CMsgStore::GetIDsFromNames returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
// IMsgStore virtual member functions implementation
//

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::Advise()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Add a notification node to the list of notification subscribers for
//      the object pointer in the entry ID passed in. In this implementation,
//      use the MAPI notification engine to call MAPI to add a
//      new notification subscriber to the object. The token returned from
//      MAPI is save in the internal list of subscribers along with the
//      notification mask. This is used when the provider's object wants to
//      send notification about them.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::Advise (ULONG			 cbEntryID,
								LPENTRYID		 pEntryID,
								ULONG			 ulEventMask,
								LPMAPIADVISESINK pAdviseSink,
								ULONG * 		 pulConnection)
{
	Log(true,"CMsgStore::Advise\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->Advise(
		cbEntryID,
		pEntryID,
		ulEventMask,
		pAdviseSink,
		pulConnection);

	Log(true,"CMsgStore::Advise returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::Unadvise()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Terminates the advise link for a particular object. The connection
//      number passed in is given to the MAPI notification engine so that it
//      removes the connection that matches it. If the MAPI successfully
//      removes the connection, remove it from the subscription list.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::Unadvise (ULONG ulConnection)
{
	Log(true,"CMsgStore::Unadvise\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->Unadvise(
		ulConnection);

	Log(true,"CMsgStore::Unadvise returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::CompareEntryIDs()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      This function compares the two entry ID structures passed in and if
//      they are pointing to the same object, meaning all the members of the
//      entry ID structure are identical, it return TRUE. Otherwise false.
//
//    Return Value
//      An HRESULT. The comparison result is returned in the
//      pulResult argument.
//
STDMETHODIMP CMsgStore::CompareEntryIDs (ULONG	   cbEntryID1,
										 LPENTRYID pEntryID1,
										 ULONG	   cbEntryID2,
										 LPENTRYID pEntryID2,
										 ULONG	   ulFlags,
										 ULONG *   pulResult)
{
	Log(true,"CMsgStore::CompareEntryIDs\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->CompareEntryIDs(
		cbEntryID1,
		pEntryID1,
		cbEntryID2,
		pEntryID2,
		ulFlags,
		pulResult);

	Log(true,"CMsgStore::CompareEntryIDs returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::OpenEntry()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      This method opens an object that exists in this message store. The object
//      could be a folder or a message in any subfolder.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::OpenEntry (ULONG	   cbEntryID,
								   LPENTRYID   pEntryID,
								   LPCIID	   pInterface,
								   ULONG	   ulFlags,
								   ULONG *	   pulObjType,
								   LPUNKNOWN * ppUnk)
{
	Log(true, "CMsgStore::OpenEntry method called!");

	HRESULT hRes = S_OK;
	ULONG ulObjType = NULL;
	LPUNKNOWN lpUnk = NULL;

	*ppUnk = NULL;

	hRes = m_pObj->OpenEntry(
		cbEntryID,
		pEntryID,
		pInterface,
		ulFlags,
		&ulObjType,
		&lpUnk );

	*pulObjType = ulObjType;

	if ( MAPI_FOLDER == ulObjType && lpUnk != NULL ) {
		*ppUnk = new CMAPIFolder( (LPMAPIFOLDER)lpUnk );
	} else {
		*ppUnk = lpUnk;
	}

	Log(true,"CMsgStore::OpenEntry, ObjType is : %d, returned 0x%08X\n", ulObjType, hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::SetReceiveFolder()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Associates a message class with a particular folder whose entry ID
//      is specified by the caller. The folder is used as the "Inbox" for
//      all messages with similar or identical message classes.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::SetReceiveFolder (LPTSTR	szMessageClass,
										  ULONG 	ulFlags,
										  ULONG 	cbEntryID,
										  LPENTRYID pEntryID)
{
	Log(true,"CMsgStore::SetReceiveFolder\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->SetReceiveFolder(
		szMessageClass,
		ulFlags,
		cbEntryID,
		pEntryID);

	Log(true,"CMsgStore::SetReceiveFolder returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::GetReceiveFolder()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Get the entry ID a receive folder for a particular message class.
//      This receive folder is used as the "inbox" folder for the particular
//      message class the caller is specifiying.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::GetReceiveFolder (LPTSTR	  szMessageClass,
										  ULONG 	  ulFlags,
										  ULONG *	  pcbEntryID,
										  LPENTRYID * ppEntryID,
										  LPTSTR *	  ppszExplicitClass)
{
	Log(true,"CMsgStore::GetReceiveFolder\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->GetReceiveFolder(
		szMessageClass,
		ulFlags,
		pcbEntryID,
		ppEntryID,
		ppszExplicitClass);

	Log(true,"CMsgStore::GetReceiveFolder returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::GetReceiveFolderTable()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Return the table of message class-to-folder mapping. This table is
//      used to decide into what folder an incoming message should be placed
//      based on its message class.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::GetReceiveFolderTable (ULONG		 ulFlags,
											   LPMAPITABLE * ppTable)
{
	Log(true,"CMsgStore::GetReceiveFolderTable\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->GetReceiveFolderTable(ulFlags, ppTable);

	Log(true,"CMsgStore::GetReceiveFolderTable returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::StoreLogoff()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      This function is used to cleanup and close up any resources owned by
//      the store since the client plans to no longer use the message store.
//      In this implementation, save the flags in the provider's object and return
//      immediately. There is no special cleanup to do.
//
//    Return Value
//      S_OK always
//
STDMETHODIMP CMsgStore::StoreLogoff (ULONG * pulFlags)
{
	Log(true,"CMsgStore::StoreLogoff\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->StoreLogoff(pulFlags);

	Log(true,"CMsgStore::StoreLogoff returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::AbortSubmit()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Stub method. In this implementation, cannot abort a submitted message.
//
//    Return Value
//      MAPI_E_UNABLE_TO_ABORT always
//
STDMETHODIMP CMsgStore::AbortSubmit (ULONG	   cbEntryID,
									 LPENTRYID pEntryID,
									 ULONG	   ulFlags)
{
	Log(true,"CMsgStore::AbortSubmit\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->AbortSubmit(cbEntryID, pEntryID, ulFlags);

	Log(true,"CMsgStore::AbortSubmit returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::GetOutgoingQueue()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Return an IMAPITable interface to the caller. This table represents
//      the outgoing queue of submitted messages. When a message is
//      submitted, a row is added to this table with the appropiate columns.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::GetOutgoingQueue (ULONG ulFlags, LPMAPITABLE * ppTable)
{
	Log(true,"CMsgStore::GetOutgoingQueue\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->GetOutgoingQueue(ulFlags, ppTable);

	Log(true,"CMsgStore::GetOutgoingQueue returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::SetLockState()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      This method is called by the spooler process to lock or unlock a
//      message for the submission process by the transports. While a
//      message is locked, the client processes cannot access this message.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::SetLockState (LPMESSAGE pMessageObj,
									  ULONG 	ulLockState)
{
	Log(true,"CMsgStore::SetLockState\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->SetLockState(pMessageObj,ulLockState);

	Log(true,"CMsgStore::SetLockState returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::FinishedMsg()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      This message is called by the spooler to let the message store do
//      the final processing after the message has been completely sent.
//      During this function, the message fixes the PR_MESSAGE_FLAGS property
//      on the submitted message and lets MAPI do final processing (i.e.
//      Delete the message) before the message is moved out of the outgoing
//      queue table.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::FinishedMsg (ULONG	   ulFlags,
									 ULONG	   cbEntryID,
									 LPENTRYID pEntryID)
{
	Log(true,"CMsgStore::FinishedMsg\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->FinishedMsg(ulFlags, cbEntryID, pEntryID);

	Log(true,"CMsgStore::FinishedMsg returned 0x%08X\n", hRes);
	return hRes;
}

///////////////////////////////////////////////////////////////////////////////
//    CMsgStore::NotifyNewMail()
//
//    Refer to the MSDN documentation for more information.
//
//    Purpose
//      Entry point for the spooler to call into the store and tell it that a
//      new message was placed in a folder. With this information, the
//      spooler-side message store notifies the client-side message store,
//      and it in turn sends a notification to the client.
//
//    Return Value
//      An HRESULT
//
STDMETHODIMP CMsgStore::NotifyNewMail (LPNOTIFICATION pNotification)
{
	Log(true,"CMsgStore::NotifyNewMail\n");
	HRESULT hRes = S_OK;

	hRes = m_pObj->NotifyNewMail(pNotification);

	Log(true,"CMsgStore::NotifyNewMail returned 0x%08X\n", hRes);
	return hRes;
}
